// -*- coding: utf-8 -*-
#ifndef MCU_DATA_SUBSCRIBER_H
#define MCU_DATA_SUBSCRIBER_H

#include <ros/ros.h>
#include <std_msgs/String.h>
#include <std_msgs/Float64.h>
#include <yaml-cpp/yaml.h>
#include <functional>
#include <map>
#include <string>
#include <mutex>

/**
 * @brief MCU数据订阅器类
 * 
 * 这个类提供了一个简单的接口，让其他ROS节点可以方便地订阅串口节点发布的MCU数据
 * 支持同步和异步两种获取数据的方式
 */
class McuDataSubscriber {
public:
    // 回调函数类型定义
    using StringCallback = std::function<void(const std::string& value)>;
    using DoubleCallback = std::function<void(double value)>;
    using ButtonCallback = std::function<void(bool pressed)>;
    
    /**
     * @brief 构造函数
     * @param nh ROS节点句柄
     */
    McuDataSubscriber(ros::NodeHandle& nh);
    
    /**
     * @brief 析构函数
     */
    ~McuDataSubscriber() = default;
    
    // === 同步数据获取接口 ===
    
    /**
     * @brief 获取字符串类型数据的最新值
     * @param key 数据键名
     * @return 最新的字符串值，如果未收到数据则返回空字符串
     */
    std::string getStringValue(const std::string& key) const;
    
    /**
     * @brief 获取数值类型数据的最新值
     * @param key 数据键名
     * @return 最新的数值，如果未收到数据则返回0.0
     */
    double getDoubleValue(const std::string& key) const;
    
    /**
     * @brief 获取按钮状态
     * @param buttonName 按钮名称 ("button_basic", "button_adv", "button_cam_calib")
     * @return 按钮是否被按下
     */
    bool getButtonState(const std::string& buttonName) const;
    
    /**
     * @brief 检查是否已接收到指定键的数据
     * @param key 数据键名
     * @return 是否有数据
     */
    bool hasData(const std::string& key) const;
    
    /**
     * @brief 获取数据的最后更新时间
     * @param key 数据键名
     * @return 最后更新时间戳
     */
    ros::Time getLastUpdateTime(const std::string& key) const;
    
    // === 异步回调接口 ===
    
    /**
     * @brief 注册字符串数据回调函数
     * @param key 数据键名
     * @param callback 回调函数
     */
    void subscribeString(const std::string& key, StringCallback callback);
    
    /**
     * @brief 注册数值数据回调函数
     * @param key 数据键名
     * @param callback 回调函数
     */
    void subscribeDouble(const std::string& key, DoubleCallback callback);
    
    /**
     * @brief 注册按钮状态回调函数
     * @param buttonName 按钮名称
     * @param callback 回调函数，参数为按钮是否被按下
     */
    void subscribeButton(const std::string& buttonName, ButtonCallback callback);
    
    /**
     * @brief 取消订阅指定键的数据
     * @param key 数据键名
     */
    void unsubscribe(const std::string& key);
    
    // === 便捷接口 ===
    
    /**
     * @brief 等待指定键的数据到达
     * @param key 数据键名
     * @param timeout_seconds 超时时间（秒），默认10秒
     * @return 是否在超时前收到数据
     */
    bool waitForData(const std::string& key, double timeout_seconds = 10.0);
    
    /**
     * @brief 获取所有已订阅的键列表
     * @return 键名列表
     */
    std::vector<std::string> getSubscribedKeys() const;
    
    /**
     * @brief 打印当前所有数据状态（调试用）
     */
    void printAllData() const;
    
    // === 数据发布接口（模拟串口接收数据） ===
    
    /**
     * @brief 发布字符串类型数据到上行话题
     * @param key 数据键名
     * @param value 字符串值
     * @return 发送是否成功
     */
    bool publishStringData(const std::string& key, const std::string& value);
    
    /**
     * @brief 发布数值类型数据到上行话题
     * @param key 数据键名
     * @param value 数值
     * @return 发送是否成功
     */
    bool publishDoubleData(const std::string& key, double value);
    
    /**
     * @brief 发布按钮状态到上行话题
     * @param buttonName 按钮名称
     * @param pressed 按钮是否被按下
     * @return 发送是否成功
     */
    bool publishButtonState(const std::string& buttonName, bool pressed);
    
    /**
     * @brief 检查指定键的发布器是否有订阅者
     * @param key 数据键名
     * @return 是否有订阅者
     */
    bool hasUpstreamSubscribers(const std::string& key) const;
    
    /**
     * @brief 获取指定键的发布器订阅者数量
     * @param key 数据键名
     * @return 订阅者数量
     */
    uint32_t getUpstreamSubscriberCount(const std::string& key) const;

private:
    ros::NodeHandle& nh_;
    
    // 数据存储
    mutable std::mutex dataMutex_;
    std::map<std::string, std::string> stringData_;
    std::map<std::string, double> doubleData_;
    std::map<std::string, ros::Time> updateTimes_;
    
    // 订阅器管理
    std::map<std::string, ros::Subscriber> subscribers_;
    
    // 发布器管理（用于模拟串口数据发布）
    std::map<std::string, ros::Publisher> publishers_;
    
    // 回调函数存储
    std::map<std::string, StringCallback> stringCallbacks_;
    std::map<std::string, DoubleCallback> doubleCallbacks_;
    std::map<std::string, ButtonCallback> buttonCallbacks_;
    
    // 预定义的数据类型映射
    std::map<std::string, std::string> dataTypes_;
    
    // 内部回调函数
    void stringTopicCallback(const std::string& key, const std_msgs::String::ConstPtr& msg);
    void doubleTopicCallback(const std::string& key, const std_msgs::Float64::ConstPtr& msg);
    
    // 初始化数据类型映射
    void initializeDataTypes();
    
    // 从配置文件加载数据类型映射
    void loadUpstreamConfig(const std::string& configFile);
    
    // 为所有已知数据键初始化默认值
    void initializeDefaultValues();
    
    // 为所有已知数据键创建订阅器
    void createAllSubscribers();
    
    // 为所有已知数据键创建发布器
    void createAllPublishers();
    
    // 创建订阅器
    void createSubscriber(const std::string& key);
    
    // 创建发布器
    void createPublisher(const std::string& key);
};

#endif // MCU_DATA_SUBSCRIBER_H
